﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using System.Runtime.InteropServices;
using Microsoft.Samples.DslTools.ConfigurationSectionDesigner.CustomCode.CodeGeneration;
using System.IO;

namespace Microsoft.Samples.DslTools.ConfigurationSectionDesigner
{
    // Inform our Package that we're providing a Code Generator
    [ProvideCodeGenerator( typeof( CsdFileGenerator ), "CsdFileGenerator",
        "Generates implementations of the configuration describled in .csd files", true )]
    internal sealed partial class ConfigurationSectionDesignerPackage
    {
    }

    [Guid( "2FF11172-2CCC-4b42-8AD0-5E6400EB8728" )]
    public class CsdFileGenerator : VsMultipleFileGenerator<string>
    {

        /// <summary>
        /// An adaptor class on the TemplatedCodeGenerator in order to
        /// access the otherwise protected method GenerateCode, which we
        /// need to access from outside the class.
        /// 
        /// This class is used to run text transformations as simply as
        /// possible.
        /// </summary>
        private class TemplateCodeGenerator : TemplatedCodeGenerator
        {
            public new byte[] GenerateCode( string inputFileName, string inputFileContent )
            {
                return base.GenerateCode( inputFileName, inputFileContent );
            }
        }

        private TemplateCodeGenerator _templateCodeGenerator;

        /// <summary>
        /// Returns a list of file extensions to generate or preserve
        /// </summary>
        /// <returns>A list of file extensions to generate or preserve</returns>
        public override IEnumerator<string> GetEnumerator()
        {
            // "Borrow" a List<>'s Enumerator to do our job for us
            List<string> fileExtensionList = new List<string>();
            fileExtensionList.Add( "cs" );
            fileExtensionList.Add( "diagram" );
            fileExtensionList.Add( "config" );
            fileExtensionList.Add( "xsd" );
            return fileExtensionList.GetEnumerator();
        }

        /// <summary>
        /// Returns the filename that matches the file extension given.
        /// </summary>
        /// <param name="fileExtension">One of the file extensions from the <see cref="GetEnumerator()"/> method.</param>
        /// <returns>The name of the input file plus the file extension.</returns>
        protected override string GetFileName( string fileExtension )
        {
            FileInfo fi = new FileInfo( InputFilePath );
            return string.Format( "{0}.{1}", fi.Name, fileExtension );
        }

        /// <summary>
        /// Generates the contents of the given file extension.
        /// If null is returned, it means to preserve the existing file instead
        /// of generating a new one.
        /// </summary>
        /// <param name="fileExtension">One of the file extensions from the <see cref="GetEnumerator()"/> method.</param>
        /// <returns>The generated content for the given file extension, or null.</returns>
        private byte[] GenerateAllContent( string fileExtension )
        {
            string inputFileContent;

            // For debugging purposes, it is better to be able to edit the .tt files without having to
            // recompile the solution to test changes. During release, use the embedded .tt files
            // instead.
            switch( fileExtension )
            {
                case "cs-def":
#if DEBUG
                    inputFileContent = File.ReadAllText( Path.GetFullPath( @"..\..\..\DslPackage\Resources\ConfigurationSectionDesignerCode.tt" ) );
#else
                    inputFileContent = UTF8Encoding.UTF8.GetString( CodeGenerationResource.ConfigurationSectionDesignerCode );
#endif
                    break;

                case "config":
#if DEBUG
                    inputFileContent = File.ReadAllText( Path.GetFullPath( @"..\..\..\DslPackage\Resources\ConfigurationSectionDesignerSample.tt" ) );
#else
                    inputFileContent = UTF8Encoding.UTF8.GetString( CodeGenerationResource.ConfigurationSectionDesignerSample );
#endif
                    break;

                case "xsd":
#if DEBUG
                    inputFileContent = File.ReadAllText( Path.GetFullPath( @"..\..\..\DslPackage\Resources\ConfigurationSectionDesignerSchema.tt" ) );
#else
                    inputFileContent = UTF8Encoding.UTF8.GetString( CodeGenerationResource.ConfigurationSectionDesignerSchema );
#endif
                    break;

                // Preserve the diagram and cs files, don't write anything to them
                case "diagram":
                case "cs":
                    return null;

                default:
                    throw new ApplicationException( "Unhandled file content" );
            }

            // Replace our input file name placeholder with the real input file name
            // so the text transformer knows which .csd file to work on.
            inputFileContent = inputFileContent.Replace( "$inputFileName$", InputFilePath );

            // Instantiate the code generator if not already done
            if( _templateCodeGenerator == null )
            {
                _templateCodeGenerator = new TemplateCodeGenerator();
                _templateCodeGenerator.SetSite( Site );
            }
            return _templateCodeGenerator.GenerateCode( InputFilePath, inputFileContent );
        }

        public override byte[] GenerateContent( string element )
        {
            return GenerateAllContent( element );
        }

        public override string GetDefaultExtension()
        {
            return ".csd.cs";
        }

        public override byte[] GenerateDefaultContent()
        {
            return GenerateAllContent( "cs-def" );
        }
    }
}
